package servlet;

import java.io.IOException;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import net.smartam.leeloo.client.request.OAuthClientRequest;
import net.smartam.leeloo.common.OAuth;
import net.smartam.leeloo.common.exception.OAuthProblemException;
import net.smartam.leeloo.common.message.types.ParameterStyle;
import net.smartam.leeloo.rs.request.OAuthAccessResourceRequest;

import org.opensaml.DefaultBootstrap;

import util.Logger;
import util.SamlRequest;
import util.SamlResponse;
import constant.Constant;

/**
 * Servlet implementation class ResourceServlet
 */
@WebServlet(Constant.DEFAULT_RESOURCESERVER)
public class ResourceServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public ResourceServlet() {
		super();
		// TODO Auto-generated constructor stub
	}

	@Override
	public void init() throws ServletException {
		super.init();
		try{
			DefaultBootstrap.bootstrap();
		}
		catch(Exception ex){
			ex.printStackTrace();
		}
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		Logger.Log("Resource Server....");

		if (!this.checkApplicationSession(request, response)) {

			// No Application Session
			if (this.checkAccessToken(request,
					request.getParameter(OAuth.OAUTH_ACCESS_TOKEN))) {
				request.getSession().setAttribute(OAuth.OAUTH_ACCESS_TOKEN,
						request.getParameter(OAuth.OAUTH_ACCESS_TOKEN));

				String application_url = "/Application.jsp?"
						+ OAuth.OAUTH_USERNAME + "=" + this.localUser;
				if (this.replaceToken != null) {
					application_url += "&" + OAuth.OAUTH_ACCESS_TOKEN + "="
							+ this.replaceToken;
				}
				request.getRequestDispatcher(application_url).forward(request, response);

				return;
			}

			try {
				// Make the OAuth Request out of this request and validate it
				// Specify where you expect OAuth access token (request header,
				// body or query string)
				OAuthAccessResourceRequest oauthRequest = new OAuthAccessResourceRequest(
						request, ParameterStyle.BODY);

				// Get the access token
				String accessToken = oauthRequest.getAccessToken();

				Logger.Log("accessToken " + accessToken);
				// ... validate access token
				if (accessToken == null) {
					this.dealwithInvalidResourceRequest(request, response);
					return;
				}

				// if something goes wrong
			} catch (OAuthProblemException ex) {
				System.err.println(ex.getMessage());
				// build error response
				// ex.printStackTrace();
				try {

					this.dealwithInvalidResourceRequest(request, response);

					return;
				} catch (Exception exe) {
					System.err.println(exe.getMessage());
				}
				// response.sendRedirect(Constant.DEFAULT_URL_AUTHSERVER);
			} catch (Exception e) {
				e.printStackTrace();
			}

		} else {
			request.getRequestDispatcher("/Application.jsp").forward(request,
					response);
		}
	}

	private void dealwithInvalidResourceRequest(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		OAuthClientRequest oauthreq = OAuthClientRequest
				.authorizationLocation(this.chooseAuthnPoint(request))
				.setClientId(request.getParameter(OAuth.OAUTH_CLIENT_ID))
				.setRedirectURI(this.getResourceURI(request))//Constant.HOST_LOCAL+Constant.DEFAULT_URL_RESOURCESERVER)//request.getParameter(Constant.SYNC_CALLBACKURI))// 
				.buildQueryMessage();
		Logger.Log("Callback URI " + request.getParameter(Constant.SYNC_CALLBACKURI));
		Logger.Log("Redirect URI " + oauthreq.getLocationUri());

		String uri = oauthreq.getLocationUri()
				+"&"+Constant.SYNC_CALLBACKURI+"="+request.getParameter(Constant.SYNC_CALLBACKURI);
		Logger.Log("Final URI " + uri);		
		
		response.setStatus(Constant.STATUSCODE_OAUTH);
		response.addHeader("Location", uri);
		/*
		 * PrintWriter pw = response.getWriter();
		 * pw.print(oauthreq.getLocationUri());
		 * //System.out.println("body "+r.getBody()); pw.flush(); pw.close();
		 */

	}
	private String getResourceURI(HttpServletRequest request)throws Exception{
		return URLEncoder.encode(request.getRequestURL().toString(), "utf-8");
	}
	private String chooseAuthnPoint(HttpServletRequest request) {
		//TODO:
		
		String ap = request.getParameter(Constant.SYNC_AP);
		String ap_url = null;
		if(Constant.AP_OTHER.equals(ap)){
			ap_url = request.getParameter(Constant.SYNC_URL_AP_OTHER) + "?";
			if (ap != null)
				ap_url += "&" + Constant.SYNC_AP + "=" + ap;
		}
		else{
			ap_url = Constant.DEFAULT_URL_AUTHSERVER + "?";//Constant.HOST_LOCAL + Constant.DEFAULT_URL_AUTHSERVER + "?";
			if (ap != null)
				ap_url += "&" + Constant.SYNC_AP + "=" + ap;
		}
		Logger.Log("AP_URI "+ap_url);
		
		return ap_url;
/*		String ap_url = Constant.HOST_LOCAL + Constant.DEFAULT_URL_AUTHSERVER + "?";
		String ap = request.getParameter(Constant.SYNC_AP);
		if (ap != null)
			ap_url += "&" + Constant.SYNC_AP + "=" + ap;
		return ap_url;*/
	}

	private boolean checkApplicationSession(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
/*		HttpSession checkSession = request.getSession(false);
		return checkSession != null
				&& checkSession.getAttribute(OAuth.OAUTH_ACCESS_TOKEN) != null;*/
		return false;
	}

	private String currentIssuer = null;
	private String currentArtifact = null;
	private String localUser = null;
	private String replaceToken = null;

	private boolean checkAccessToken(HttpServletRequest request, String token) {
		Logger.Log("token to check " + token);
		this.currentIssuer = null;
		this.currentArtifact = null;
		this.localUser = null;
		this.replaceToken = null;
		if (token == null)
			return false;
		try {
			
			SamlResponse saml = new SamlResponse();
			saml.decode(token);
			
			if (!this.checkIssuerValidation(saml.getIssuer()))
				return false;

			this.currentIssuer = saml.getIssuer();
			Logger.Log("Issuer " + this.currentIssuer);
			this.currentArtifact = saml.getArtifact();
			Logger.Log("Artifact " + this.currentArtifact);
			this.checkUserFromIDFS(this.currentArtifact);
			Logger.Log("User " + this.localUser);
			Logger.Log("replaceToken " + this.replaceToken);
			

		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return token != null;
	}

	protected boolean checkIssuerValidation(String issuer) {
		// TODO:
		if (Constant.AP_HP.equals(issuer) || Constant.AP_OTHER.equals(issuer))
			return true;
		return false;
	}

	protected boolean checkUserFromIDFS(String artifact) throws Exception {
		// TODO:

		this.localUser = "mike.beiter@" + this.currentIssuer;

		boolean isalocaluser = Constant.AP_HP.equals(this.currentIssuer);
		Logger.Log("is a local user " + isalocaluser);

		if (!isalocaluser) {
			// request another user and
			// this.localUser = "mikes.private@domain.invalid";

			// request the new token.
			this.replaceToken = this.generateSamlToken(Constant.AP_HP,
					"ReplacedNewToken");
		}

		return true;
	}

	private String generateSamlToken(String issuer, String access_token)
			throws Exception {
		return SamlRequest.request(issuer, access_token);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		this.doGet(request, response);
	}

}
